/* Copyright (c) 2025 Beijing Semidrive Technology Corporation
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/** ************************************************************************************************
 * @file     Xspi_Bus.c                                                                           *
 * @brief    LLD xXSPI bus Driver                                                                       *
 * @author   Department/Semidrive                                                                     *
 * @date     2025/02/25                                                             *
 *                                                                                                 *
 **************************************************************************************************/

#include "Xspi_Bus.h"

#ifdef __cplusplus
extern "C" {
#endif /* #ifdef __cplusplus */

/* PRQA S 0314,0316,1505,1881,3673,4404 EOF */

/***************************************************************************************************
 *                                 Private Macro definition                                        *
 **************************************************************************************************/
#define XSPI_BUS_INIT_REFCLK_HZ         (20000000u)
#define XSPI_BUS_ERASE_TIMEOUT_US       (20000000U)
#define XSPI_BUS_WRITE_TIMEOUT_US       (1000U)

#define XSPI_BUS_REG_RDWR_IGNOR_ADDRESS (0xFFFFFFFFu)

/* Flash opcodes. */
#define XSPI_BUS_OP_WREN (0x06U)       /* Write enable */
#define XSPI_BUS_OP_WRDI (0x04U)       /* Write disable */
#define XSPI_BUS_OP_RDSR (0x05U)       /* Read status register */
#define XSPI_BUS_OP_GIGA_RDSR (0x35U)  /* Read status register for giga */
#define XSPI_BUS_OP_GIGA_WRSR (0x31U)  /* Write status register for giga */
#define XSPI_BUS_OP_WRSR (0x01U)       /* Write status register 1 byte */
#define XSPI_BUS_OP_RDSR2 (0x3fU)      /* Read status register 2 */
#define XSPI_BUS_OP_WRSR2 (0x3eU)      /* Write status register 2 */
#define XSPI_BUS_OP_WRPR (0xC0U)  /* Write parameters register */
#define XSPI_BUS_OP_READ (0x03U)       /* Read data bytes (low frequency) */
#define XSPI_BUS_OP_READ_FAST (0x0bU)  /* Read data bytes (high frequency) */
#define XSPI_BUS_OP_READ_1_1_2 (0x3bU) /* Read data bytes (Dual Output SPI) */
#define XSPI_BUS_OP_READ_1_2_2 (0xbbU) /* Read data bytes (Dual I/O SPI) */
#define XSPI_BUS_OP_READ_1_1_4 (0x6bU) /* Read data bytes (Quad Output SPI) */
#define XSPI_BUS_OP_READ_1_4_4 (0xebU) /* Read data bytes (Quad I/O SPI) */
#define XSPI_BUS_OP_READ_1_1_8 (0x8bU) /* Read data bytes (Octal Output SPI) */
#define XSPI_BUS_OP_READ_1_8_8 (0xcbU) /* Read data bytes (Octal I/O SPI) */
#define XSPI_BUS_OP_PP (0x02U)         /* Page program (up to 256 bytes) */
#define XSPI_BUS_OP_PP_1_1_4 (0x32U)   /* Quad page program */
#define XSPI_BUS_OP_PP_1_4_4 (0x38U)   /* Quad page program */
#define XSPI_BUS_OP_PP_1_1_8 (0x82U)   /* Octal page program */
#define XSPI_BUS_OP_PP_1_8_8 (0xc2U)   /* Octal page program */
#define XSPI_BUS_OP_BE_4K (0x20U)      /* Erase 4KiB block */
#define XSPI_BUS_OP_BE_4K_PMC (0xd7U)  /* Erase 4KiB block on PMC chips */
#define XSPI_BUS_OP_BE_32K (0x52U)     /* Erase 32KiB block */
#define XSPI_BUS_OP_CHIP_ERASE (0xc7U) /* Erase whole flash chip */
#define XSPI_BUS_OP_SE (0xd8U)         /* Sector erase (usually 64KiB) */
#define XSPI_BUS_OP_RDID (0x9fU)       /* Read JEDEC ID */
#define XSPI_BUS_OP_RDSFDP (0x5aU)     /* Read SFDP */
#define XSPI_BUS_OP_RDCR (0x35U)       /* Read configuration register */
#define XSPI_BUS_OP_RDFSR (0x70U)      /* Read flag status register */
#define XSPI_BUS_OP_CLFSR (0x50U)      /* Clear flag status register */
#define XSPI_BUS_OP_RDEAR (0xc8U)      /* Read Extended Address Register */
#define XSPI_BUS_OP_WREAR (0xc5U)      /* Write Extended Address Register */
#define XSPI_BUS_OP_RSTEN (0x66U)      /* Reset Enable register */
#define XSPI_BUS_OP_RSTMEM (0x99U)     /* Reset Memory */
#define XSPI_BUS_OP_EN4B (0xb7U)       /* Enter 4-byte mode */
#define XSPI_BUS_OP_EX4B (0xe9U)       /* Exit 4-byte mode */
#define XSPI_BUS_OP_RDAR (0x65U)       /* Read Any Register */
#define XSPI_BUS_OP_WRAR (0x71U)       /* Write Any Register */
#define XSPI_BUS_OP_WRVR (0x81U)       /* Write Volatile Register */
#define XSPI_BUS_OP_RDVR (0x85U)       /* Write Volatile Register */
#define XSPI_BUS_OP_ISSI_QPI_EN (0x35U) /* Enables the Flash device for QPI mode */
#define XSPI_BUS_OP_ISSI_QPI_EX (0xF5U) /* Disable the Flash device from QPI mode */
#define XSPI_BUS_OP_GIGA_QPI_EN (0x38U) /* Enables the Flash device for QPI mode */
#define XSPI_BUS_OP_GIGA_QPI_EX (0xFFU) /* Disable the Flash device from QPI mode */

/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
#define XSPI_BUS_OP_READ_4B (0x13U)       /* Read data bytes (low frequency) */
#define XSPI_BUS_OP_READ_FAST_4B (0x0cU)  /* Read data bytes (high frequency) */
#define XSPI_BUS_OP_READ_1_1_2_4B (0x3cU) /* Read data bytes (Dual Output SPI) */
#define XSPI_BUS_OP_READ_1_2_2_4B (0xbcU) /* Read data bytes (Dual I/O SPI) */
#define XSPI_BUS_OP_READ_1_1_4_4B (0x6cU) /* Read data bytes (Quad Output SPI) */
#define XSPI_BUS_OP_READ_1_4_4_4B (0xecU) /* Read data bytes (Quad I/O SPI) */
#define XSPI_BUS_OP_READ_1_1_8_4B (0x7cU) /* Read data bytes (Octal Output SPI) */
#define XSPI_BUS_OP_READ_1_8_8_4B (0xccU) /* Read data bytes (Octal I/O SPI) */
#define XSPI_BUS_OP_PP_4B (0x12U)         /* Page program (up to 256 bytes) */
#define XSPI_BUS_OP_PP_1_1_4_4B (0x34U)   /* Quad page program */
#define XSPI_BUS_OP_PP_1_4_4_4B (0x3eU)   /* Quad page program */
#define XSPI_BUS_OP_PP_1_1_8_4B (0x84U)   /* Octal page program */
#define XSPI_BUS_OP_PP_1_8_8_4B (0x8eU)   /* Octal page program */
#define XSPI_BUS_OP_BE_4K_4B (0x21U)      /* Erase 4KiB block */
#define XSPI_BUS_OP_BE_32K_4B (0x5cU)     /* Erase 32KiB block */
#define XSPI_BUS_OP_SE_4B (0xdcU)         /* Sector erase (usually 64KiB) */

/* Miron */
#define XSPI_BUS_OCTAL_DQS_MIRION (0xe7U)
#define XSPI_BUS_OCTAL_NON_DQS_MIRION (0xc7U)

/* macronix */
#define XSPI_BUS_OP_WRVR_MACRONIX (0x72U)       /* Write Volatile Register */
#define XSPI_BUS_OP_RDVR_MACRONIX (0x71U)       /* Read Volatile Register */
#define XSPI_BUS_OP_WRCR_MACRONIX (0x1U)       /* Write flash configuration Register */
#define XSPI_BUS_OP_RDCR_MACRONIX (0x15U)       /* Read flash configuration Register */

/* issi giga */
#define XSPI_BUS_OP_READ_4S_4D_4D (0xedU)

/* giga */
#define XSPI_BUS_OP_PP_1_4_4_GIGA (0xc2U)

/* cycess */
#define XSPI_BUS_OP_EX4B_CYCESS (0xB8U)

/* Double Transfer Rate opcodes - defined in JEDEC JESD216B. */
#define XSPI_BUS_OP_READ_1_1_1_DTR (0x0dU)
#define XSPI_BUS_OP_READ_1_2_2_DTR (0xbdU)
#define XSPI_BUS_OP_READ_1_4_4_DTR (0xedU)
#define XSPI_BUS_OP_READ_1_1_8_DTR (0x9dU)

#define XSPI_BUS_OP_READ_1_1_1_DTR_4B (0x0eU)
#define XSPI_BUS_OP_READ_1_2_2_DTR_4B (0xbeU)
#define XSPI_BUS_OP_READ_1_4_4_DTR_4B (0xeeU)
#define XSPI_BUS_OP_READ_1_8_8_DTR_4B (0xfdU)

#define XSPI_BUS_ID_CAPACITY_OFFSET 2

#define XSPI_BUS_TRAINING_BUF_SIZE (32u)

/* opcode is replaced with dummy */
#define XSPI_BUS_READ_ID_PROTO(dummy, proto) XSPI_IP_PROTO_OPCODE(dummy, proto)

/***************************************************************************************************
 *                                    Private Type Declarations                                    *
 **************************************************************************************************/
typedef struct
{
    Xspi_BusHandleType *bus;
    uint32_t trainingAddress;
} Xspi_BusTrainingArgType;

/***************************************************************************************************
 *                                  Private Function Declarations                                  *
 **************************************************************************************************/
static int32_t Xspi_BusMironDefaultInit(Xspi_BusHandleType *bus, uint8_t cs);
static int32_t Xspi_BusMacronixDefaultInit(Xspi_BusHandleType *bus, uint8_t cs);
static int32_t Xspi_BusMacronixGetBankStatus(struct Xspi_BusHandle *bus, uint8_t cs, uint32_t addr, uint32_t length);
static int32_t Xspi_BusIssiQuadDefaultInit(Xspi_BusHandleType *bus, uint8_t cs);
static int32_t Xspi_BusGigaQuadDefaultInit(Xspi_BusHandleType *bus, uint8_t cs);
static int32_t Xspi_BusGiga25QuadDefaultInit(Xspi_BusHandleType *bus, uint8_t cs);
static int32_t Xspi_BusCypressDefaultInit(Xspi_BusHandleType *bus, uint8_t cs);

/***************************************************************************************************
 *                                  Private Variable Definitions                                   *
 **************************************************************************************************/
#ifdef CFG_PLATFORM_MCAL
#define FLS_START_SEC_CONST_UNSPECIFIED
#include "Fls_MemMap.h"
#endif /* #ifdef CFG_PLATFORM_MCAL */

/*PRQA S 3218  1*/
static const Xspi_BusDeviceInfoType Xspi_BusSupportFlashTab[] = {
    /* miron */
    {
        .name = "mt35xu",
        .flashId = {0x2c, 0x5b, 0, 0, 0, 0},
        .readProto.u32 = XSPI_IP_PROTO_OPCODE(XSPI_BUS_OP_READ_1_8_8, XSPI_BUS_PROTO_8D_8D_8D),
        .writeProto.u32 = XSPI_IP_PROTO_OPCODE(XSPI_BUS_OP_PP_1_8_8, XSPI_BUS_PROTO_8D_8D_8D),
        .eraseOpcode = {
            XSPI_BUS_OP_BE_4K,  /* erase 4Kb */
            XSPI_BUS_OP_BE_32K,  /* erase 32Kb */
            0,  /* erase 64Kb, do not support this mode */
            XSPI_BUS_OP_SE,  /* erase 128Kb */
            0,  /* erase 256Kb, do not support this mode */
        },
        .readDummy = 20u,
        .statusDummy = 8u,
        .writeDummy = 0u,
        .pageSize = 256u,
        .defaultInit = Xspi_BusMironDefaultInit,
        .setProtectArea = NULL_PTR,
        .getBankStatus = NULL_PTR,
        .enterXipMode = NULL_PTR,
    },
    {
        .name = "mt35xl",
        .flashId = {0x2c, 0x5a, 0, 0, 0, 0},
        .readProto.u32 = XSPI_IP_PROTO_OPCODE(XSPI_BUS_OP_READ_1_8_8, XSPI_BUS_PROTO_8D_8D_8D),
        .writeProto.u32 = XSPI_IP_PROTO_OPCODE(XSPI_BUS_OP_PP_1_8_8, XSPI_BUS_PROTO_8D_8D_8D),
        .eraseOpcode = {
            XSPI_BUS_OP_BE_4K,  /* erase 4Kb */
            XSPI_BUS_OP_BE_32K,  /* erase 32Kb */
            0,  /* erase 64Kb, do not support this mode */
            XSPI_BUS_OP_SE,  /* erase 128Kb */
            0,  /* erase 256Kb, do not support this mode */
        },
        .readDummy = 20u,
        .statusDummy = 8u,
        .writeDummy = 0u,
        .pageSize = 256u,
        .defaultInit = Xspi_BusMironDefaultInit,
        .setProtectArea = NULL_PTR,
        .getBankStatus = NULL_PTR,
        .enterXipMode = NULL_PTR,
    },
    {
        .name = "is25xl",
        .flashId = {0x9d, 0x5a, 0, 0, 0, 0},
        .readProto.u32 = XSPI_IP_PROTO_OPCODE(XSPI_BUS_OP_READ_1_8_8, XSPI_BUS_PROTO_8D_8D_8D),
        .writeProto.u32 = XSPI_IP_PROTO_OPCODE(XSPI_BUS_OP_PP_1_8_8, XSPI_BUS_PROTO_8D_8D_8D),
        .eraseOpcode = {
            XSPI_BUS_OP_BE_4K,  /* erase 4Kb */
            XSPI_BUS_OP_BE_32K,  /* erase 32Kb */
            0,  /* erase 64Kb, do not support this mode */
            XSPI_BUS_OP_SE,  /* erase 128Kb */
            0,  /* erase 256Kb, do not support this mode */
        },
        .readDummy = 20u,
        .statusDummy = 8u,
        .writeDummy = 0u,
        .pageSize = 256u,
         .defaultInit = Xspi_BusMironDefaultInit,
        .setProtectArea = NULL_PTR,
        .getBankStatus = NULL_PTR,
        .enterXipMode = NULL_PTR,
    },
    {
        .name = "gd25lx",
        .flashId = {0xc8, 0x68, 0, 0, 0, 0},
        .readProto.u32 = XSPI_IP_PROTO_OPCODE(XSPI_BUS_OP_READ_1_8_8, XSPI_BUS_PROTO_8D_8D_8D),
        .writeProto.u32 = XSPI_IP_PROTO_OPCODE(XSPI_BUS_OP_PP_1_8_8, XSPI_BUS_PROTO_8D_8D_8D),
        .eraseOpcode = {
            XSPI_BUS_OP_BE_4K,  /* erase 4Kb */
            XSPI_BUS_OP_BE_32K,  /* erase 32Kb */
            XSPI_BUS_OP_SE,  /* erase 64Kb */
            0,  /* erase 128Kb, do not support this mode */
            0,  /* erase 256Kb, do not support this mode */
        },
        .readDummy = 14u,
        .statusDummy = 8u,
        .writeDummy = 0u,
        .pageSize = 256u,
         .defaultInit = Xspi_BusMironDefaultInit,
        .setProtectArea = NULL_PTR,
        .getBankStatus = NULL_PTR,
        .enterXipMode = NULL_PTR,
    },
    /* macronix */
    {
        .name = "mx25uw",
        .flashId = {0xc2, 0x84, 0, 0, 0, 0},
        .readProto.u32 = XSPI_IP_PROTO_OPCODE(0xEEu, XSPI_BUS_PROTO_8D_8D_8D_INVERT),
        .writeProto.u32 = XSPI_IP_PROTO_OPCODE(XSPI_BUS_OP_PP_4B, XSPI_BUS_PROTO_8D_8D_8D_INVERT),
        .eraseOpcode = {
            XSPI_BUS_OP_BE_4K_4B,  /* erase 4Kb */
            0,  /* erase 32Kb, do not support this mode  */
            XSPI_BUS_OP_SE_4B,  /* erase 64Kb*/
            0,  /* erase 128Kb, do not support this mode  */
            0,  /* erase 256Kb, do not support this mode */
        },
        .readDummy = 20u,
        .statusDummy = 4u,
        .writeDummy = 0u,
        .pageSize = 256u,
        .defaultInit = Xspi_BusMacronixDefaultInit,
        .setProtectArea = NULL_PTR,
        .getBankStatus = Xspi_BusMacronixGetBankStatus,
        .enterXipMode = NULL_PTR,
    },
    {
        .name = "mx25uw",
        .flashId = {0xc2, 0x81, 0, 0, 0, 0},
        .readProto.u32 = XSPI_IP_PROTO_OPCODE(0xEEu, XSPI_BUS_PROTO_8D_8D_8D_INVERT),
        .writeProto.u32 = XSPI_IP_PROTO_OPCODE(XSPI_BUS_OP_PP_4B, XSPI_BUS_PROTO_8D_8D_8D_INVERT),
        .eraseOpcode = {
            XSPI_BUS_OP_BE_4K_4B,  /* erase 4Kb */
            0,  /* erase 32Kb, do not support this mode  */
            XSPI_BUS_OP_SE_4B,  /* erase 64Kb*/
            0,  /* erase 128Kb, do not support this mode  */
            0,  /* erase 256Kb, do not support this mode */
        },
        .readDummy = 20u,
        .statusDummy = 4u,
        .writeDummy = 0u,
        .pageSize = 256u,
        .defaultInit = Xspi_BusMacronixDefaultInit,
        .setProtectArea = NULL_PTR,
        .getBankStatus = Xspi_BusMacronixGetBankStatus,
        .enterXipMode = NULL_PTR,
    },
    /* issi */
    {
        .name = "is25wp",
        .flashId = {0x9d, 0x70, 0, 0, 0, 0},
        .readProto.u32 = XSPI_IP_PROTO_OPCODE(XSPI_BUS_OP_READ_1_4_4, XSPI_BUS_PROTO_4S_4S_4S),
        .writeProto.u32 = XSPI_IP_PROTO_OPCODE(XSPI_BUS_OP_PP, XSPI_BUS_PROTO_4S_4S_4S),
        .eraseOpcode = {
            XSPI_BUS_OP_BE_4K,  /* erase 4Kb */
            XSPI_BUS_OP_BE_32K,  /* erase 32Kb */
            XSPI_BUS_OP_SE,  /* erase 64Kb */
            0,  /* erase 128Kb, do not support this mode */
            0,  /* erase 256Kb, do not support this mode */
        },
        .readDummy = 14u,
        .statusDummy = 0u,
        .writeDummy = 0u,
        .pageSize = 256u,
        .defaultInit = Xspi_BusIssiQuadDefaultInit,
        .setProtectArea = NULL_PTR,
        .getBankStatus = NULL_PTR,
        .enterXipMode = NULL_PTR,
    },
    {
        .name = "is25lp",
        .flashId = {0x9d, 0x60, 0, 0, 0, 0},
        .readProto.u32 = XSPI_IP_PROTO_OPCODE(XSPI_BUS_OP_READ_1_4_4, XSPI_BUS_PROTO_4S_4S_4S),
        .writeProto.u32 = XSPI_IP_PROTO_OPCODE(XSPI_BUS_OP_PP, XSPI_BUS_PROTO_4S_4S_4S),
        .eraseOpcode = {
            XSPI_BUS_OP_BE_4K,  /* erase 4Kb */
            XSPI_BUS_OP_BE_32K,  /* erase 32Kb */
            XSPI_BUS_OP_SE,  /* erase 64Kb */
            0,  /* erase 128Kb, do not support this mode */
            0,  /* erase 256Kb, do not support this mode */
        },
        .readDummy = 14u,
        .statusDummy = 0u,
        .writeDummy = 0u,
        .pageSize = 256u,
        .defaultInit = Xspi_BusIssiQuadDefaultInit,
        .setProtectArea = NULL_PTR,
        .getBankStatus = NULL_PTR,
        .enterXipMode = NULL_PTR,
    },
    /* giga */
    {
        .name = "gd25lb",
        .flashId = {0xc8, 0x67, 0, 0, 0, 0},
        .readProto.u32 = XSPI_IP_PROTO_OPCODE(XSPI_BUS_OP_READ_1_1_4, XSPI_BUS_PROTO_4S_4S_4S),
        .writeProto.u32 = XSPI_IP_PROTO_OPCODE(XSPI_BUS_OP_PP_1_4_4_GIGA, XSPI_BUS_PROTO_4S_4S_4S),
        .eraseOpcode = {
            XSPI_BUS_OP_BE_4K,  /* erase 4Kb */
            XSPI_BUS_OP_BE_32K,  /* erase 32Kb */
            XSPI_BUS_OP_SE,  /* erase 64Kb */
            0,  /* erase 128Kb, do not support this mode */
            0,  /* erase 256Kb, do not support this mode */
        },
        .readDummy = 10u,
        .statusDummy = 0u,
        .writeDummy = 0u,
        .pageSize = 256u,
        .defaultInit = Xspi_BusGigaQuadDefaultInit,
        .setProtectArea = NULL_PTR,
        .getBankStatus = NULL_PTR,
        .enterXipMode = NULL_PTR,
    },
    {
        .name = "gd25q",
        .flashId = {0xc8, 0x40, 0, 0, 0, 0},
        .readProto.u32 = XSPI_IP_PROTO_OPCODE(XSPI_BUS_OP_READ_1_1_4, XSPI_BUS_PROTO_1S_1S_4S),
        .writeProto.u32 = XSPI_IP_PROTO_OPCODE(XSPI_BUS_OP_PP_1_1_4, XSPI_BUS_PROTO_1S_1S_4S),
        .eraseOpcode = {
            XSPI_BUS_OP_BE_4K,  /* erase 4Kb */
            XSPI_BUS_OP_BE_32K,  /* erase 32Kb */
            XSPI_BUS_OP_SE,  /* erase 64Kb */
            0,  /* erase 128Kb, do not support this mode */
            0,  /* erase 256Kb, do not support this mode */
        },
        .readDummy = 8u,
        .statusDummy = 0u,
        .writeDummy = 0u,
        .pageSize = 256u,
        .defaultInit = Xspi_BusGiga25QuadDefaultInit,
        .setProtectArea = NULL_PTR,
        .getBankStatus = NULL_PTR,
        .enterXipMode = NULL_PTR,
    },
    {
        .name = "gd25f",
        .flashId = {0xc8, 0x43, 0, 0, 0, 0},
        .readProto.u32 = XSPI_IP_PROTO_OPCODE(XSPI_BUS_OP_READ_1_4_4_DTR, XSPI_BUS_PROTO_1S_4D_4D),
        .writeProto.u32 = XSPI_IP_PROTO_OPCODE(XSPI_BUS_OP_PP_1_1_4, XSPI_BUS_PROTO_1S_1S_4S),
        .eraseOpcode = {
            XSPI_BUS_OP_BE_4K,  /* erase 4Kb */
            XSPI_BUS_OP_BE_32K,  /* erase 32Kb */
            XSPI_BUS_OP_SE,  /* erase 64Kb */
            0,  /* erase 128Kb, do not support this mode */
            0,  /* erase 256Kb, do not support this mode */
        },
        .readDummy = 8u,
        .statusDummy = 0u,
        .writeDummy = 0u,
        .pageSize = 256u,
        .defaultInit = Xspi_BusGiga25QuadDefaultInit,
        .setProtectArea = NULL_PTR,
        .getBankStatus = NULL_PTR,
        .enterXipMode = NULL_PTR,
    },
    {
        .name = "w25q",
        .flashId = {0xef, 0x40, 0, 0, 0, 0},
        .readProto.u32 = XSPI_IP_PROTO_OPCODE(XSPI_BUS_OP_READ_1_1_4, XSPI_BUS_PROTO_1S_1S_4S),
        .writeProto.u32 = XSPI_IP_PROTO_OPCODE(XSPI_BUS_OP_PP_1_1_4, XSPI_BUS_PROTO_1S_1S_4S),
        .eraseOpcode = {
            XSPI_BUS_OP_BE_4K,  /* erase 4Kb */
            XSPI_BUS_OP_BE_32K,  /* erase 32Kb */
            XSPI_BUS_OP_SE,  /* erase 64Kb */
            0,  /* erase 128Kb, do not support this mode */
            0,  /* erase 256Kb, do not support this mode */
        },
        .readDummy = 8u,
        .statusDummy = 0u,
        .writeDummy = 0u,
        .pageSize = 256u,
        .defaultInit = Xspi_BusGiga25QuadDefaultInit,
        .setProtectArea = NULL_PTR,
        .getBankStatus = NULL_PTR,
        .enterXipMode = NULL_PTR,
    },
    /* Cypress */
    {
        .name = "S25fl",
        .flashId = {0x01, 0x60, 0, 0, 0, 0},
        .readProto.u32 = XSPI_IP_PROTO_OPCODE(XSPI_BUS_OP_READ_FAST, XSPI_BUS_PROTO_1S_1S_1S),
        .writeProto.u32 = XSPI_IP_PROTO_OPCODE(XSPI_BUS_OP_PP, XSPI_BUS_PROTO_1S_1S_1S),
        .eraseOpcode = {
            XSPI_BUS_OP_BE_4K,  /* erase 4Kb */
            XSPI_BUS_OP_BE_32K,  /* erase 32Kb */
            XSPI_BUS_OP_SE,  /* erase 64Kb */
            0,  /* erase 128Kb, do not support this mode */
            0,  /* erase 256Kb, do not support this mode */
        },
        .readDummy = 8u,
        .statusDummy = 0u,
        .writeDummy = 0u,
        .pageSize = 256u,
        .defaultInit = NULL_PTR,
        .setProtectArea = NULL_PTR,
        .getBankStatus = NULL_PTR,
        .enterXipMode = NULL_PTR,
    },
    {
        .name = "S28hl",
        .flashId = {0x34, 0x5a, 0, 0, 0, 0},
        .readProto.u32 = XSPI_IP_PROTO_OPCODE(XSPI_BUS_OP_READ_1_4_4_DTR_4B, XSPI_BUS_PROTO_8D_8D_8D),
        .writeProto.u32 = XSPI_IP_PROTO_OPCODE(XSPI_BUS_OP_PP_4B, XSPI_BUS_PROTO_8D_8D_8D),
        .eraseOpcode = {
            0,  /* erase 4Kb, do not support this mode  */
            0,  /* erase 32Kb, do not support this mode  */
            0,  /* erase 64Kb, do not support this mode  */
            0,  /* erase 128Kb, do not support this mode */
            XSPI_BUS_OP_SE_4B,  /* erase 256Kb*/
        },
        .readDummy = 23u,
        .statusDummy = 8u,
        .writeDummy = 0u,
        .pageSize = 256u,
        .defaultInit = Xspi_BusCypressDefaultInit,
        .setProtectArea = NULL_PTR,
        .getBankStatus = NULL_PTR,
        .enterXipMode = NULL_PTR,
    },
};

static VAR_ALIGN(const uint8_t Xspi_BusTrainingPattern[XSPI_BUS_TRAINING_BUF_SIZE], 64u) =
{
    0x44, 0x1c, 0x39, 0x05, 0xd3, 0x7a, 0x3c, 0x04,
    0x16, 0x42, 0x0c, 0x8b, 0x7d, 0x12, 0x89, 0xa2,
    0xb8, 0xb1, 0xf7, 0xe8, 0xb7, 0x49, 0xca, 0x1c,
    0xaa, 0x9b, 0xf2, 0x7e, 0x01, 0x97, 0x60, 0x8c
};

#ifdef CFG_PLATFORM_MCAL
#define FLS_STOP_SEC_CONST_UNSPECIFIED
#include "Fls_MemMap.h"
#endif /* #ifdef CFG_PLATFORM_MCAL */

#ifdef CFG_PLATFORM_MCAL
#define FLS_START_SEC_CODE
#include "Fls_MemMap.h"
#endif /* #ifdef CFG_PLATFORM_MCAL */

/***************************************************************************************************
 *                                  Private Function Definition                                  *
 **************************************************************************************************/
static uint8_t Xspi_BusMatchCsNum(Xspi_BusHandleType *bus, uint32_t addr, uint32_t size, uint32_t *overlapSize)
{
    uint8_t csNum = XSPI_BUS_MAX_CS_NUM;
    uint8_t i;

    if ((addr + size) > bus->totalSize)
    {
        XSPI_LOG_ERROR("Parammer error (0x%x vs 0x%x)\r\n", addr + size, bus->totalSize);
    }
    else
    {
        for (i = 0u; i < bus->csNum; i++)
        {
            if ((addr >= bus->device[i].offset) &&
                (addr < (bus->device[i].offset + bus->device[i].size)))
            {
                csNum = i;
                if (overlapSize != NULL)
                {
                    *overlapSize = XSPI_MIN(size,
                        bus->device[i].offset + bus->device[i].size - addr);
                }
            }
        }
    }

    return csNum;
}

static inline int32_t Xspi_BusRegRead(Xspi_BusHandleType *bus, uint8_t cs,
    Xspi_IpCommandType *readCmd, uint32_t addr, uint8_t* buf, uint32_t size)
{
    return Xspi_IpCommandRead(bus->host, readCmd, addr + bus->device[cs].offset, buf, size);
}

static inline int32_t Xspi_BusRegWrite(Xspi_BusHandleType *bus, uint8_t cs,
    Xspi_IpCommandType *writeCmd, uint32_t addr, const uint8_t* buf, uint32_t size)
{
    return Xspi_IpCommandWrite(bus->host, writeCmd, addr + bus->device[cs].offset, buf, size);
}

static int32_t Xspi_BusCmdSend(Xspi_BusHandleType *bus, uint8_t cs, uint8_t opcode)
{
    Xspi_IpCommandType writeCmd;
    /* PRQA S 0315 1 */
    (void) XSPI_MEMCLR(&writeCmd, sizeof(Xspi_IpCommandType));
    writeCmd.proto.sketch.proto = XSPI_IP_PROTO_ADDRESS_DATA_DIS(bus->device[cs].regProto.sketch.proto);
    writeCmd.proto.sketch.opcode = opcode;
    writeCmd.dummy = 0u;
    writeCmd.addrBytes = 0u;
    return Xspi_BusRegWrite(bus, cs, &writeCmd, 0u, NULL_PTR, 0u);
}

static int32_t Xspi_BusWriteEnable(Xspi_BusHandleType *bus, uint8_t cs, bool enable)
{
    return Xspi_BusCmdSend(bus, cs, (true == enable) ? XSPI_BUS_OP_WREN : XSPI_BUS_OP_WRDI);
}

static int32_t Xspi_BusRegisterRead(Xspi_BusHandleType *bus, uint8_t cs,
    uint8_t opcode, uint8_t dummy, uint32_t addr, uint8_t *valueRet)
{
    int32_t ret = XSPI_RET_FAILED;
    VAR_ALIGN(uint8_t data[2], 4);
    Xspi_IpCommandType readCmd;
    uint32_t regAddr;

    /* PRQA S 0315 1 */
    (void) XSPI_MEMCLR(&readCmd, sizeof(Xspi_IpCommandType));

    readCmd.proto.sketch.proto = bus->device[cs].regProto.sketch.proto;
    readCmd.proto.sketch.opcode = opcode;
    readCmd.dummy = dummy;
    if (addr == XSPI_BUS_REG_RDWR_IGNOR_ADDRESS)
    {
        regAddr = 0u;
        readCmd.proto.detail.addressDis = 1u;
    }
    else
    {
        regAddr = addr;
        readCmd.addrBytes = bus->device[cs].addrWidth;
    }

    if (XSPI_RET_OK == Xspi_BusRegRead(bus, cs, &readCmd, regAddr, data,
                        (readCmd.proto.detail.dataRate == 1u) ? 2u : 1u))
    {
        *valueRet = data[0];
        XSPI_LOG_DEBUG("[%d %d] - RD(0x%x): 0x%x 0x%x\r\n", bus->host->config.id, cs,
            opcode, regAddr, data[0]);
        ret = XSPI_RET_OK;
    }

    return ret;
}

static int32_t Xspi_BusRegisterWrite(Xspi_BusHandleType *bus, uint8_t cs,
    uint8_t opcode, uint32_t addr, uint8_t value)
{
    int32_t ret = XSPI_RET_FAILED;
    VAR_ALIGN(uint8_t data[2], 4);
    Xspi_IpCommandType writeCmd;
    uint32_t regAddr;

    /* PRQA S 0315 1 */
    (void) XSPI_MEMCLR(&writeCmd, sizeof(Xspi_IpCommandType));
    writeCmd.proto.sketch.proto = bus->device[cs].regProto.sketch.proto;
    writeCmd.proto.sketch.opcode = opcode;
    writeCmd.dummy = 0u;
    if (addr == XSPI_BUS_REG_RDWR_IGNOR_ADDRESS)
    {
        regAddr = 0u;
        writeCmd.proto.detail.addressDis = 1u;
    }
    else
    {
        regAddr = addr;
        writeCmd.addrBytes = bus->device[cs].addrWidth;
    }
    data[0] = value;
    data[1] = value;

    if (XSPI_RET_OK != Xspi_BusWriteEnable(bus, cs, true))
    {
        /* Do nothing */
    }
    else if (XSPI_RET_OK != Xspi_BusRegWrite(bus, cs, &writeCmd, regAddr, data,
                        (writeCmd.proto.detail.dataRate == 1u) ? 2u : 1u))
    {
        /* Do nothing */
    }
    else
    {
        XSPI_LOG_DEBUG("[%d %d] - WR(0x%x): 0x%x 0x%x\r\n", bus->host->config.id, cs,
            opcode, regAddr, data[0]);
        ret = XSPI_RET_OK;
    }

    return ret;
}

static int32_t Xspi_BusGetBusyStatus(Xspi_BusHandleType *bus, uint8_t cs)
{
    int32_t ret = XSPI_RET_FAILED;
    uint8_t status;

    if (XSPI_RET_OK == Xspi_BusRegisterRead(bus, cs, XSPI_BUS_OP_RDSR,
            bus->device[cs].info.statusDummy,
            (bus->device[cs].regProto.detail.invertOpcodeEn == 1u) ?
            0u : XSPI_BUS_REG_RDWR_IGNOR_ADDRESS, &status))
    {
        if (0U == (status & XSPI_BIT(0)))
        {
            ret = XSPI_RET_OK;
        }
        else
        {
            ret = XSPI_RET_BUSY;
        }
    }

    return ret;
}

static int32_t Xspi_BusWaitIdle(Xspi_BusHandleType *bus, uint8_t cs, uint32_t timeout)
{
    int32_t ret = XSPI_RET_OK;
    uint32 tickCount;

    for (tickCount = 0; tickCount < timeout; tickCount++)
    {
        ret = Xspi_BusGetBusyStatus(bus, cs);
         if (XSPI_RET_BUSY == ret)
        {
            XSPI_UDELAY(1);
        }
        else
        {
            break;
        }
    }

    return ret;
}

static int32_t Xspi_BusWaitBlockIdle(Xspi_BusHandleType *bus, uint32_t address, uint32_t size, uint32_t timeout)
{
    int32_t ret = XSPI_RET_OK;
    uint32_t overlapSize = 0u;
    uint32_t currentStartAddr = address;
    uint8_t cs;

    while ((currentStartAddr < (address + size)) && (ret == XSPI_RET_OK))
    {
        cs = Xspi_BusMatchCsNum(bus, address, size, &overlapSize);
        if ((XSPI_BUS_MAX_CS_NUM == cs) || (overlapSize == 0u))
        {
            ret = XSPI_RET_PARAMETER_ERR;
        }
        else if (XSPI_RET_OK != Xspi_BusWaitIdle(bus, cs, timeout))
        {
            ret = XSPI_RET_TIMEOUT;
        }
        else
        {
            currentStartAddr += overlapSize;
        }
    }
    return ret;
}

static int32_t Xspi_BusAddrWidth4ByteEnable(Xspi_BusHandleType *bus, uint8_t cs)
{
    int32_t ret = XSPI_RET_OK;
    if ((bus->device[cs].addrWidth == 3u) && (bus->device[cs].size > 0x1000000U))
    {
        ret = Xspi_BusCmdSend(bus, cs, XSPI_BUS_OP_EN4B);
        if (ret == XSPI_RET_OK)
        {
            bus->device[cs].addrWidth = 4u;
        }
    }

    return ret;
}

/*****************************************************************************************/
static int32_t Xspi_BusMironDummySetup(Xspi_BusHandleType *bus, uint8_t cs)
{
    int32_t ret = XSPI_RET_FAILED;
    uint8_t reg;

    if (XSPI_RET_OK != Xspi_BusRegisterWrite(bus, cs, XSPI_BUS_OP_WRVR, 1u, bus->device[cs].info.readDummy))
    {
        /* Do nothing */
    }
    else if (XSPI_RET_OK != Xspi_BusRegisterRead(bus, cs, XSPI_BUS_OP_RDVR, 8u, 1u, &reg))
    {
        /* Do nothing */
    }
    else if (reg != bus->device[cs].info.readDummy)
    {
        /* Do nothing */
    }
    else
    {
        ret = XSPI_RET_OK;
    }
    return ret;
}

static int32_t Xspi_BusMironOctalDtrSetup(Xspi_BusHandleType *bus, uint8_t cs)
{
    int32_t ret = XSPI_RET_FAILED;
    bool octalDtrEnable = false;
    uint8_t writeReg = 0xFFu;
    uint8_t readReg = 0u;

    if ((XSPI_IP_OCTAL_LANS == bus->device[cs].info.readProto.detail.comandLans) &&
        (XSPI_IP_DDR == bus->device[cs].info.readProto.detail.comamdRate))
    {
        octalDtrEnable = true;
        writeReg = XSPI_BUS_OCTAL_DQS_MIRION; //XSPI_BUS_OCTAL_NON_DQS_MIRION; //XSPI_BUS_OCTAL_DQS_MIRION;
    }

    if (XSPI_RET_OK != Xspi_BusRegisterWrite(bus, cs, XSPI_BUS_OP_WRVR, 0u, writeReg))
    {
        /* Do nothing */
    }
    else
    {
        /* Enable or disable OPI mode */
        Xspi_IpDqsEnable(bus->host, octalDtrEnable);
        bus->device[cs].regProto.sketch.proto = (true == octalDtrEnable) ?
            XSPI_BUS_PROTO_8D_8D_8D : XSPI_BUS_PROTO_1S_1S_1S;
        bus->device[cs].addrWidth = (true == octalDtrEnable) ? 4u : 3u;

        /* Read-back check */
        if (XSPI_RET_OK != Xspi_BusRegisterRead(bus, cs, XSPI_BUS_OP_RDVR, 8u, 0u, &readReg))
        {
            /* Do nothing */
        }
        else if (writeReg != readReg)
        {
            /* Do nothing */
        }
        else
        {
            ret = XSPI_RET_OK;
        }
    }

    return ret;
}

static int32_t Xspi_BusMironDefaultInit(Xspi_BusHandleType *bus, uint8_t cs)
{
    int32_t ret = XSPI_RET_FAILED;
    /* Step0. Special configuration */
    /* Not necessary */

    /* Step1. dummy setup */
    if (XSPI_RET_OK != Xspi_BusMironDummySetup(bus, cs))
    {
        /* Do nothing */
    }
    /* Step2. Address width setup */
    else if (XSPI_RET_OK != Xspi_BusAddrWidth4ByteEnable(bus, cs))
    {
        /* Do nothing */
    }
    /* Step3. Octal-spi mode setup */
    else if (XSPI_RET_OK != Xspi_BusMironOctalDtrSetup(bus, cs))
    {
        /* Do nothing */
    }
    else
    {
        ret = XSPI_RET_OK;
    }
    return ret;
}

static int32_t Xspi_BusMacronixOctalDtrSetup(Xspi_BusHandleType *bus, uint8_t cs)
{
    int32_t ret = XSPI_RET_FAILED;
    uint8_t dsConfig = 0x3u; // DS 41ohms
    uint8_t tryCnt = 2;
    bool octalDtrEnable = false;
    uint8_t readReg;
    uint8_t writeReg;

    if ((XSPI_IP_OCTAL_LANS == bus->device[cs].info.readProto.detail.comandLans) &&
        (XSPI_IP_DDR == bus->device[cs].info.readProto.detail.comamdRate))
    {
        octalDtrEnable = true;
    }
    writeReg = (TRUE == octalDtrEnable) ? 0x2u : 0x0u;

    if (XSPI_RET_OK != Xspi_BusRegisterWrite(bus, cs, XSPI_BUS_OP_WRVR_MACRONIX, 0u, writeReg))
    {
        /* Do nothing */
    }
    else
    {
        /* Enable or disable OPI mode */
        Xspi_IpDqsEnable(bus->host, octalDtrEnable);
        bus->device[cs].regProto.sketch.proto = (true == octalDtrEnable) ?
            XSPI_BUS_PROTO_8D_8D_8D_INVERT : XSPI_BUS_PROTO_1S_1S_1S;
        bus->device[cs].addrWidth = (true == octalDtrEnable) ? 4u : 3u;

        /* Read-back check */
        if (XSPI_RET_OK != Xspi_BusRegisterRead(bus, cs, XSPI_BUS_OP_RDVR_MACRONIX, 8u, 0u, &readReg))
        {
            /* Do nothing */
        }
        else if (writeReg != readReg)
        {
            /* Do nothing */
        }
        else
        {
            ret = XSPI_RET_OK;

            if (bus->device[cs].info.flashId[1] == 0x81u)
            {
                Xspi_IpByteSwapSetup(bus->host, octalDtrEnable);
            }
        }
    }

    if ((true == octalDtrEnable) && (XSPI_RET_OK == ret)) {
        while ((tryCnt > 0u) && (XSPI_RET_OK == ret))
        {
            if (XSPI_RET_OK != Xspi_BusRegisterRead(bus, cs, XSPI_BUS_OP_RDCR_MACRONIX, 4u, 1u, &readReg))
            {
                ret = XSPI_RET_FAILED;
            }
            else if ((readReg & 0x07u) == dsConfig)
            {
                tryCnt = 0;
            }
            else
            {
                writeReg = (readReg & ~0x7u) | dsConfig;
                if (XSPI_RET_OK != Xspi_BusRegisterWrite(bus, cs, XSPI_BUS_OP_WRCR_MACRONIX, 1u, writeReg))
                {
                    ret = XSPI_RET_FAILED;
                }
                tryCnt--;
            }
        }
    }

    return ret;
}

static int32_t Xspi_BusMacronixDefaultInit(Xspi_BusHandleType *bus, uint8_t cs)
{
    int32_t ret = XSPI_RET_FAILED;
    /* Step0. Special configuration */
    /* Not necessary */

    /* Step1. dummy setup */
    /* Not necessary */

    /* Step2. Address width setup */
    /* Not necessary */

    /* Step3. Octal-spi mode setup */
    if (XSPI_RET_OK != Xspi_BusMacronixOctalDtrSetup(bus, cs))
    {
        /* Do nothing */
    }
    else
    {
        ret = XSPI_RET_OK;
    }

    return ret;
}

static int32_t Xspi_BusMacronixGetBankStatus(struct Xspi_BusHandle *bus, uint8_t cs, uint32_t addr, uint32_t length)
{
    int32_t ret = XSPI_RET_OK;
    VAR_ALIGN(uint8 data[3], 4U);
    uint32 i, startBit, endBit;
    uint32 bankSize = bus->device[cs].size >> 2;
    Xspi_IpCommandType readCmd;

    /* PRQA S 0315 1 */
    (void) XSPI_MEMCLR(&readCmd, sizeof(Xspi_IpCommandType));
    readCmd.proto.sketch.proto = bus->device[cs].regProto.sketch.proto;
    readCmd.proto.sketch.opcode = XSPI_BUS_OP_RDVR_MACRONIX;
    readCmd.proto.detail.modeBitEn = 1u;
    readCmd.dummy = 4u;
    readCmd.addrBytes = 2u;
    readCmd.modeCode = 0x0001u;

    if (XSPI_RET_OK != Xspi_BusRegRead(bus, cs, &readCmd, 0x8000u, data,
                        (readCmd.proto.detail.dataRate == 1u) ? 2u : 1u))
    {
        ret = XSPI_RET_FAILED;
        XSPI_LOG_ERROR("[%d %d] - RDVR: 0x8000 + mode(0x0001)u Failed\r\n", bus->host->config.id, cs);
    }
    else if (0u != (data[0] & 0xFu))
    {
        startBit = addr / bankSize;
        endBit = XSPI_DIV_ROUND_UP(addr + length, bankSize);

        XSPI_LOG_DEBUG("Bank status is 0x%x, read banks[%d:%d]\r\n", data[0], startBit, endBit);

        for (i = startBit; i < endBit; i++)
        {
            if (0u != (data[0] & XSPI_BIT(i)))
            {
                ret = XSPI_RET_BUSY; /* program/erase operation in current bank */
                XSPI_LOG_DEBUG("bank-%d is in program/erase (0x%x 0x%x)\r\n", i, addr, length);
                break;
            }
        }
    }
    else
    {
        /* Do nothing */
    }

    return ret;
}

static int32_t Xspi_BusIssiQuadDummySetup(Xspi_BusHandleType *bus, uint8_t cs)
{
    int32_t ret = XSPI_RET_FAILED;
    uint8_t dummyConfig;

    if ((bus->device[cs].size == 0x800000U) &&
        (bus->device[cs].info.flashId[0] == 0x9du) &&
        (bus->device[cs].info.flashId[1] == 0x60u))
    {
        /* Fix up for IS25LP064A. Refer to descriptions in part.h */
        bus->device[cs].info.readDummy = 10U;
        dummyConfig = 0x03u << 3;
    }
    else
    {
        dummyConfig = bus->device[cs].info.readDummy << 3;
    }

    if (XSPI_RET_OK != Xspi_BusRegisterWrite(bus, cs, XSPI_BUS_OP_WRPR,
                        XSPI_BUS_REG_RDWR_IGNOR_ADDRESS, dummyConfig))
    {
        /* Do nothing */
    }
    else
    {
        ret = XSPI_RET_OK;
    }
    return ret;
}

static int32_t Xspi_BusIssiQuadSetup(Xspi_BusHandleType *bus, uint8_t cs)
{
    int32_t ret = XSPI_RET_FAILED;
    uint8_t reg;

    /* Quad enable */
    if (bus->device[cs].info.readProto.detail.dataLans == XSPI_IP_QUAD_LANS)
    {
        if (XSPI_RET_OK != Xspi_BusRegisterRead(bus, cs, XSPI_BUS_OP_RDSR, 0u,
                            XSPI_BUS_REG_RDWR_IGNOR_ADDRESS, &reg))
        {
            /* Do nothing */
        }
        else if (0u == (reg & XSPI_BIT(6)))
        {
            if (XSPI_RET_OK != Xspi_BusRegisterWrite(bus, cs, XSPI_BUS_OP_WRSR,
                    XSPI_BUS_REG_RDWR_IGNOR_ADDRESS, reg | (uint8_t)XSPI_BIT(6)))
            {
                /* Do nothing */
            }
            else if (XSPI_RET_OK != Xspi_BusWaitIdle(bus, cs, XSPI_BUS_WRITE_TIMEOUT_US))
            {
                /* Do nothing */
            }
            else
            {
                ret = XSPI_RET_OK;
            }
        }
        else
        {
            ret = XSPI_RET_OK;
        }
    }

    /* Enter/Exit quad-spi mode */
    if (XSPI_RET_OK != ret)
    {
        /* Do nothing */
    }
    else if (bus->device[cs].regProto.detail.comandLans ==
            bus->device[cs].info.readProto.detail.comandLans)
    {
        /* Do nothing, Already in Expected mode */
    }
    else if (XSPI_RET_OK != Xspi_BusCmdSend(bus, cs,
        (bus->device[cs].info.readProto.detail.comandLans == XSPI_IP_QUAD_LANS) ?
        XSPI_BUS_OP_ISSI_QPI_EN : XSPI_BUS_OP_ISSI_QPI_EX))
    {
        ret = XSPI_RET_FAILED;
    }
    else
    {
        bus->device[cs].regProto.u32 =
                (bus->device[cs].info.readProto.detail.comandLans == XSPI_IP_QUAD_LANS) ?
                    XSPI_BUS_PROTO_4S_4S_4S : XSPI_BUS_PROTO_1S_1S_1S;
    }

    return ret;
}

static int32_t Xspi_BusIssiQuadDefaultInit(Xspi_BusHandleType *bus, uint8_t cs)
{
    int32_t ret = XSPI_RET_FAILED;
    /* Step0. Special configuration */
    /* Not necessary */

    /* Step1. dummy setup */
    if (XSPI_RET_OK != Xspi_BusIssiQuadDummySetup(bus, cs))
    {
        /* Do nothing */
    }
    /* Step2. Address width setup */
    else if (XSPI_RET_OK != Xspi_BusAddrWidth4ByteEnable(bus, cs))
    {
        /* Do nothing */
    }
    /* Step3. Quad-spi mode setup */
    else if (XSPI_RET_OK != Xspi_BusIssiQuadSetup(bus, cs))
    {
        /* Do nothing */
    }
    else
    {
        ret = XSPI_RET_OK;
    }

    return ret;
}

static int32_t Xspi_BusGigaQuadDummySetup(Xspi_BusHandleType *bus, uint8_t cs)
{
    /* Exactly the same process And the register address of the operation */
    return Xspi_BusMironDummySetup(bus, cs);
}

static int32_t Xspi_BusGigaQuadSetup(Xspi_BusHandleType *bus, uint8_t cs)
{
    int32_t ret = XSPI_RET_FAILED;

    /* Quad enable */
    /* Not necessary */

    /* Enter/Exit quad-spi mode */
    if (bus->device[cs].regProto.detail.comandLans ==
            bus->device[cs].info.readProto.detail.comandLans)
    {
        /* Do nothing, Already in Expected mode */
    }
    else if (XSPI_RET_OK != Xspi_BusCmdSend(bus, cs,
        (bus->device[cs].info.readProto.detail.comandLans == XSPI_IP_QUAD_LANS) ?
        XSPI_BUS_OP_GIGA_QPI_EN : XSPI_BUS_OP_GIGA_QPI_EX))
    {
         /* Do nothing */
    }
    else
    {
        bus->device[cs].regProto.u32 =
            (bus->device[cs].info.readProto.detail.comandLans == XSPI_IP_QUAD_LANS) ?
                XSPI_BUS_PROTO_4S_4S_4S : XSPI_BUS_PROTO_1S_1S_1S;
        ret = XSPI_RET_OK;
    }

    return ret;
}

static int32_t Xspi_BusGigaQuadDefaultInit(Xspi_BusHandleType *bus, uint8_t cs)
{
    int32_t ret = XSPI_RET_FAILED;
    /* Step0. Special configuration */
    /* Not necessary */

    /* Step1. dummy setup */
    if (XSPI_RET_OK != Xspi_BusGigaQuadDummySetup(bus, cs))
    {
        /* Do nothing */
    }
    /* Step2. Address width setup */
    else if (XSPI_RET_OK != Xspi_BusAddrWidth4ByteEnable(bus, cs))
    {
        /* Do nothing */
    }
    /* Step3. Quad-spi mode setup */
    else if (XSPI_RET_OK != Xspi_BusGigaQuadSetup(bus, cs))
    {
        /* Do nothing */
    }
    else
    {
        ret = XSPI_RET_OK;
    }

    return ret;
}

static int32_t Xspi_BusGiga25q40cQuadSetup(Xspi_BusHandleType *bus, uint8_t cs)
{
    int32_t ret = XSPI_RET_FAILED;
    VAR_ALIGN(uint8_t data[2], 4);
    Xspi_IpCommandType writeCmd;

    /* PRQA S 0315 1 */
    (void) XSPI_MEMCLR(&writeCmd,sizeof(Xspi_IpCommandType));
    writeCmd.proto.sketch.proto = bus->device[cs].regProto.sketch.proto;
    writeCmd.proto.sketch.opcode = XSPI_BUS_OP_WRSR;
    writeCmd.dummy = 0u;
    writeCmd.addrBytes = 0u;

    if (XSPI_RET_OK != Xspi_BusRegisterRead(bus, cs, XSPI_BUS_OP_GIGA_RDSR, 0u,
                            XSPI_BUS_REG_RDWR_IGNOR_ADDRESS, &data[1]))
    {
        /* Do nothing */
    }
    else if (0u == (data[1] & XSPI_BIT(1)))
    {
        data[1] |= (uint8_t)XSPI_BIT(1);
        if (XSPI_RET_OK != Xspi_BusRegisterRead(bus, cs, XSPI_BUS_OP_RDSR, 0u,
                            XSPI_BUS_REG_RDWR_IGNOR_ADDRESS, &data[0]))
        {
            /* Do nothing */
        }
        else if (XSPI_RET_OK != Xspi_BusWriteEnable(bus, cs, true))
        {
            /* Do nothing */
        }
        else if (XSPI_RET_OK != Xspi_BusRegRead(bus, cs, &writeCmd, 0, data, 2u))
        {
            /* Do nothing */
        }
        else if (XSPI_RET_OK != Xspi_BusWaitIdle(bus, cs, XSPI_BUS_WRITE_TIMEOUT_US))
        {
            /* Do nothing */
        }
        else
        {
            ret = XSPI_RET_OK;
        }
    }
    else
    {
        ret = XSPI_RET_OK;
    }
    return ret;
}

static int32_t Xspi_BusGiga25QuadSetup(Xspi_BusHandleType *bus, uint8_t cs)
{
    int32_t ret = XSPI_RET_FAILED;
    uint8_t reg;

    /* Quad enable */
    if ((bus->device[cs].info.flashId[1] == 0x40u) && (bus->device[cs].size == 0x80000U))
    {
        /* For gd25q40 */
        ret = Xspi_BusGiga25q40cQuadSetup(bus, cs);
    }
    else if (bus->device[cs].info.readProto.detail.dataLans == XSPI_IP_QUAD_LANS)
    {
        if (XSPI_RET_OK != Xspi_BusRegisterRead(bus, cs, XSPI_BUS_OP_GIGA_RDSR, 0u,
                            XSPI_BUS_REG_RDWR_IGNOR_ADDRESS, &reg))
        {
            /* Do nothing */
        }
        else if (0u == (reg & XSPI_BIT(1)))
        {
            if (XSPI_RET_OK != Xspi_BusRegisterWrite(bus, cs, XSPI_BUS_OP_GIGA_WRSR,
                            XSPI_BUS_REG_RDWR_IGNOR_ADDRESS, reg | (uint8_t)XSPI_BIT(1)))
            {
                /* Do nothing */
            }
            else if (XSPI_RET_OK != Xspi_BusWaitIdle(bus, cs, XSPI_BUS_WRITE_TIMEOUT_US))
            {
                /* Do nothing */
            }
            else
            {
                ret = XSPI_RET_OK;
            }

        }
        else
        {
            ret = XSPI_RET_OK;
        }
    }
    else
    {
        ret = XSPI_RET_OK;
    }

    /* Enter/Exit quad-spi mode */
    /* nonsupport */

    return ret;
}

static int32_t Xspi_BusGiga25QuadDefaultInit(Xspi_BusHandleType *bus, uint8_t cs)
{
    int32_t ret = XSPI_RET_FAILED;
    /* Step0. Special configuration */
    /* Not necessary */

    /* Step1. dummy setup */
    /* Not necessary */

    /* Step2. Address width setup */
    if (XSPI_RET_OK != Xspi_BusAddrWidth4ByteEnable(bus, cs))
    {
        /* Do nothing */
    }
    /* Step3. Quad-spi mode setup */
    if (XSPI_RET_OK != Xspi_BusGiga25QuadSetup(bus, cs))
    {
        /* Do nothing */
    }
    else
    {
        ret = XSPI_RET_OK;
    }

    return ret;
}

static int32_t Xspi_BusCypressSpecialConfig(Xspi_BusHandleType *bus, uint8_t cs)
{
    int32_t ret = XSPI_RET_FAILED;
    uint8_t reg;
    uint8_t readReg = 0u;

    /* Select 256K erase sector */
    if (XSPI_RET_OK != Xspi_BusRegisterRead(bus, cs, XSPI_BUS_OP_RDAR, 0u, 0x800004U, &reg))
    {
        /* Do nothing */
    }
    else if (0u == (reg & XSPI_BIT(3)))
    {
        if (XSPI_RET_OK != Xspi_BusRegisterWrite(bus, cs, XSPI_BUS_OP_WRAR, 0x800004U, reg | (uint8_t)XSPI_BIT(3)))
        {
            /* Do nothing */
        }
        else if (XSPI_RET_OK != Xspi_BusWaitIdle(bus, cs, XSPI_BUS_WRITE_TIMEOUT_US))
        {
            /* Do nothing */
        }
        else if (XSPI_RET_OK != Xspi_BusRegisterRead(bus, cs, XSPI_BUS_OP_RDAR, 0u, 0x800004U, &readReg))
        {
            /* Do nothing */
        }
        else if (reg != readReg)
        {
            XSPI_LOG_DEBUG("Select 256K erase sector failed, read data = 0x%x\r\n", readReg);
        }
        else
        {
            ret = XSPI_RET_OK;
        }
    }
    else
    {
        ret = XSPI_RET_OK;
    }

    /* Disable 2-bit Ecc */
    if (ret != XSPI_RET_OK)
    {
        /* Do nothing */
    }
    else if (XSPI_RET_OK != Xspi_BusRegisterRead(bus, cs, XSPI_BUS_OP_RDAR, 0u, 0x800005U, &reg))
    {
        ret = XSPI_RET_FAILED;
    }
    else if (0u != (reg & XSPI_BIT(3)))
    {
        ret = XSPI_RET_FAILED;
        reg &= (uint8_t)(~XSPI_BIT(3));
        if (XSPI_RET_OK != Xspi_BusWriteEnable(bus, cs, true))
        {
            /* Do nothing */
        }
        else if (XSPI_RET_OK != Xspi_BusRegisterWrite(bus, cs, XSPI_BUS_OP_WRAR, 0x05u, reg))
        {
            /* Do nothing */
        }
        else if (XSPI_RET_OK != Xspi_BusWaitIdle(bus, cs, XSPI_BUS_WRITE_TIMEOUT_US))
        {
            /* Do nothing */
        }
        else if (XSPI_RET_OK != Xspi_BusRegisterRead(bus, cs, XSPI_BUS_OP_RDAR, 0u, 0x800005U, &readReg))
        {
            /* Do nothing */
        }
        else if (reg != readReg)
        {
            XSPI_LOG_DEBUG("Disable 2bit ECC failed, read data = 0x%x\r\n", readReg);
        }
        else
        {
            ret = XSPI_RET_OK;
        }
    }
    else
    {
        /* Do nothing */
    }

    return ret;
}

static int32_t Xspi_BusCypressOctalDtrSetup(Xspi_BusHandleType *bus, uint8_t cs)
{
    int32_t ret = XSPI_RET_FAILED;
    bool octalDtrEnable = false;
    uint8_t readReg = 0u;

    if ((XSPI_IP_OCTAL_LANS == bus->device[cs].info.readProto.detail.comandLans) &&
        (XSPI_IP_DDR == bus->device[cs].info.readProto.detail.comamdRate))
    {
        octalDtrEnable = true;
    }

    if (XSPI_RET_OK != Xspi_BusRegisterRead(bus, cs, XSPI_BUS_OP_RDAR, 8u, 0x800006U, &readReg))
    {
        /* Do nothing */
    }
    else if (XSPI_RET_OK != Xspi_BusWriteEnable(bus, cs, true))
    {
        /* Do nothing */
    }
    else if (XSPI_RET_OK != Xspi_BusRegisterWrite(bus, cs, XSPI_BUS_OP_WRAR, 0x800006U,
                (octalDtrEnable == true) ? (readReg | 0x3u) : 0u))
    {
        /* Do nothing */
    }
    else
    {
        /* Enable or disable OPI mode */
        Xspi_IpDqsEnable(bus->host, octalDtrEnable);
        bus->device[cs].regProto.sketch.proto = (true == octalDtrEnable) ?
            XSPI_BUS_PROTO_8D_8D_8D : XSPI_BUS_PROTO_1S_1S_1S;
        bus->device[cs].addrWidth = (true == octalDtrEnable) ? 4u : 3u;

        /* Read-back check */
        if (XSPI_RET_OK != Xspi_BusRegisterRead(bus, cs, XSPI_BUS_OP_RDAR, 8u, 0x800006U, &readReg))
        {
            /* Do nothing */
        }
        else if ((readReg & 0x3u) != 0x3u)
        {
            /* Do nothing */
        }
        else
        {
            ret = XSPI_RET_OK;
        }
    }

    return ret;
}

static int32_t Xspi_BusCypressDefaultInit(Xspi_BusHandleType *bus, uint8_t cs)
{
    int32_t ret = XSPI_RET_FAILED;
    /* Step0. Special configuration */
    if (XSPI_RET_OK != Xspi_BusCypressSpecialConfig(bus, cs))
    {
        /* Do nothing */
    }
    /* Step1. dummy setup */
    /* Not necessary */
    /* Step2. Address width setup */
    else if (XSPI_RET_OK != Xspi_BusAddrWidth4ByteEnable(bus, cs))
    {
        /* Do nothing */
    }
    /* Step3. Quad-spi mode setup */
    else if (XSPI_RET_OK != Xspi_BusCypressOctalDtrSetup(bus, cs))
    {
        /* Do nothing */
    }
    else
    {
        ret = XSPI_RET_OK;
    }

    return ret;
}

/** *****************************************************************************************************
 * \brief This function match Flash based on its ID.
 *
 * \verbatim
 * Syntax             : int32_t Xspi_BusFlashDetect(
 *                          Xspi_BusHandleType *bus, uint8_t cs)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : bus - Pointer to bus handler
 *                      cs - The number of cs
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : 0 - Flash has been matched
 *                     !0 - No matching Flash
 *
 * Description        : Get flash id with the spibus protocol, And match from the support list
 * \endverbatim
 * Traceability       : SWSR SWSR_XSPI_021
 *******************************************************************************************************/
static int32_t Xspi_BusFlashDetect(Xspi_BusHandleType *bus, uint8_t cs)
{
    uint8_t i, j;
    uint32_t busReadProtoList[6];
    Xspi_IpCommandType readCmd;
    VAR_ALIGN(uint8_t idBuffer[8], 4);
    bool hasDetected = false;

    /* PRQA S 0315 1 */
    (void) XSPI_MEMCLR(&readCmd, sizeof(Xspi_IpCommandType));
    busReadProtoList[0] = XSPI_BUS_READ_ID_PROTO(0U, XSPI_BUS_PROTO_1S_0S_1S);
    busReadProtoList[1] = XSPI_BUS_READ_ID_PROTO(0U, XSPI_BUS_PROTO_2S_0S_2S);
    busReadProtoList[2] = XSPI_BUS_READ_ID_PROTO(0U, XSPI_BUS_PROTO_4S_0S_4S);
    busReadProtoList[3] = XSPI_BUS_READ_ID_PROTO(8U, XSPI_BUS_PROTO_8D_0D_8D);
    busReadProtoList[4] = XSPI_BUS_READ_ID_PROTO(8U, XSPI_BUS_PROTO_8D_8D_8D);
    busReadProtoList[5] = XSPI_BUS_READ_ID_PROTO(4U, XSPI_BUS_PROTO_8D_8D_8D_INVERT);

    readCmd.proto.sketch.opcode = XSPI_BUS_OP_RDID;
    for (i = 0; (i < XSPI_ARRAY_SIZE(busReadProtoList)) && (false == hasDetected); i++)
    {
        readCmd.proto.sketch.proto = XSPI_IP_PROTO_MASK(busReadProtoList[i]);
        readCmd.dummy = (uint8_t)XSPI_IP_OPCODE_MASK(busReadProtoList[i]); /* opcode is replaced with dummy */
        if (readCmd.proto.detail.addressDis == 1u)
        {
            readCmd.addrBytes = 0u;
        }
        else
        {
            readCmd.addrBytes = (readCmd.proto.detail.addressRate == 1u) ? 4u : 3u;
        }

        if (readCmd.proto.detail.invertOpcodeEn == 1u)
        {
            Xspi_IpDqsEnable(bus->host, true);
        }
        XSPI_LOG_DEBUG("[%d %d] - dqs: %d, addrBytes = %d, dummy = %d, proto = %x\r\n",
            bus->host->config.id, cs, bus->host->dqsEnable,
            readCmd.addrBytes, readCmd.dummy, readCmd.proto.u32);

        if (XSPI_RET_OK == Xspi_BusRegRead(bus, cs, &readCmd, 0u, idBuffer, XSPI_BUS_MAX_ID_LEN))
        {
            XSPI_LOG_DEBUG("Read flash id: 0x%x 0x%x 0x%x\r\n", idBuffer[0], idBuffer[1], idBuffer[2]);
            for (j = 0u; j < XSPI_ARRAY_SIZE(Xspi_BusSupportFlashTab); j++)
            {
                if ((idBuffer[0] == Xspi_BusSupportFlashTab[j].flashId[0]) &&
                    (idBuffer[1] == Xspi_BusSupportFlashTab[j].flashId[1]))
                {
                    (void) Xspi_Memcopy((void*)&bus->device[cs].info,
                        (const void*)&Xspi_BusSupportFlashTab[j], sizeof(Xspi_BusDeviceInfoType));
                    bus->device[cs].size = ((uint32_t)1U) << (idBuffer[XSPI_BUS_ID_CAPACITY_OFFSET] & XSPI_IP_DEV_SIZE_MASK);
                    bus->device[cs].regProto.sketch.proto = XSPI_IP_PROTO_ADDRESS(
                            readCmd.proto.detail.comandLans, readCmd.proto.detail.comandLans, readCmd.proto.sketch.proto);
                    hasDetected = true;
                    break;
                }
            }
        }
        else
        {
            XSPI_LOG_ERROR("Read error\r\n");
            break;
        }
    }

    return (true == hasDetected) ? XSPI_RET_OK : XSPI_RET_FAILED;
}

static int32_t Xspi_BusTrainingPatternCheck(void * arg)
{
    int32_t ret = XSPI_RET_FAILED;
    const Xspi_BusTrainingArgType *trainingArg = (Xspi_BusTrainingArgType *)arg;
    VAR_ALIGN(uint8_t trainingBuffer[XSPI_BUS_TRAINING_BUF_SIZE], 4U);

    if (XSPI_RET_OK != Xspi_BusRead(trainingArg->bus, trainingArg->trainingAddress, trainingBuffer, XSPI_BUS_TRAINING_BUF_SIZE))
    {
        /* Do nothing */
    }
    /* PRQA S 1863 1 */
    else if (0 != XSPI_MEMCMP((const void*)trainingBuffer, (const void*)Xspi_BusTrainingPattern, XSPI_BUS_TRAINING_BUF_SIZE))
    {
        /* Do nothing */
    }
    else
    {
        ret = XSPI_RET_OK;
    }
    return ret;
}

static int32_t Xspi_BusFlashInit(Xspi_BusHandleType *bus, uint8_t cs)
{
    int32_t ret = XSPI_RET_OK;
    int32_t i;

    /* Setup flash configuration */
    bus->device[cs].cs = cs;
    bus->device[cs].sectorSize = 0x1000u; /* Default */
    for (i = 0; i < XSPI_BUS_SECTOR_TYPE_MAX; i++)
    {
        /* Select the smallest sector size, which is then used as an alignment requirement */
        if (bus->device[cs].info.eraseOpcode[i] != 0u)
        {
            /* PRQA S 1890,4534 1 */
            bus->device[cs].sectorSize = XSPI_BUS_SECTOR_TYPE_TO_SIZE(i);
            break;
        }
    }
    Xspi_IpSizeSetup(bus->host, cs, bus->device[cs].size);
    bus->device[cs].offset = bus->totalSize;
    bus->totalSize += bus->device[cs].size;
    bus->csNum++;
    bus->device[cs].addrWidth = (bus->device[cs].regProto.detail.addressRate == 1u) ? 4u : 3u;

    if (bus->device[cs].info.defaultInit != NULL)
    {
        ret = bus->device[cs].info.defaultInit(bus, cs);
    }

    return ret;
}

/* PRQA S 2755 1 */
static int32_t Xspi_BusTraining(Xspi_BusHandleType *bus, uint8_t cs)
{
    int32_t ret;
    Xspi_BusTrainingArgType trainingArg;

    /* PRQA S 0315 1 */
    (void) XSPI_MEMCLR(&trainingArg, sizeof(Xspi_BusTrainingArgType));

    trainingArg.trainingAddress = bus->device[cs].sectorSize + bus->device[cs].offset;
    trainingArg.bus = bus;

    /* Configures the command for initializing read and write data */
    Xspi_IpCommandSetup(bus->host,
        bus->device[cs].info.readProto.u32, bus->device[cs].info.readDummy,
        bus->device[cs].info.writeProto.u32, bus->device[cs].info.writeDummy,
        bus->device[cs].addrWidth);
    Xspi_IpPotoSetup(bus->host);

    /* Training prepare */
    if (XSPI_RET_OK != Xspi_BusTrainingPatternCheck((void*)&trainingArg))
    {
        if (XSPI_RET_OK != Xspi_BusEraseSync(bus, trainingArg.trainingAddress, bus->device[cs].sectorSize))
        {
            /* Do nothing */
        }
        else if (XSPI_RET_OK != Xspi_BusWriteSync(bus, trainingArg.trainingAddress, Xspi_BusTrainingPattern, XSPI_BUS_TRAINING_BUF_SIZE))
        {
            /* Do nothing */
        }
        else
        {
            /* Do nothing */
        }
    }

    (void) Xspi_IpRefclkSetup(bus->host, bus->host->config.refClkHz);
    ret = Xspi_IpTraining(bus->host, Xspi_BusTrainingPatternCheck, (void*)&trainingArg);
    return ret;
}

static int32_t Xspi_BusMatchOptimumEraseCmd(Xspi_BusHandleType *bus,
    uint8_t cs, uint32_t addr, uint32_t size, uint8_t *opcode, uint32_t *sectorSize)
{
    int32_t ret = XSPI_RET_PARAMETER_ERR;
    uint32_t sectorSizeTemp;
    int32_t i;

    for (i = XSPI_BUS_SECTOR_256KB_TYPE; i >= 0; i--)
    {
        /* PRQA S 1890,4534 1 */
        sectorSizeTemp = XSPI_BUS_SECTOR_TYPE_TO_SIZE(i);
        if ((sectorSizeTemp <= size) &&
            (0u != bus->device[cs].info.eraseOpcode[i]) &&
            (XSPI_IS_ALIGNED(addr, sectorSizeTemp)))
        {
            *opcode = bus->device[cs].info.eraseOpcode[i];
            *sectorSize = sectorSizeTemp;
            ret = XSPI_RET_OK;
            break;
        }
    }

    return ret;
}

static void Xspi_BusRomConfigSetup(Xspi_BusHandleType *bus, const Xspi_BusConfigType *busConfig)
{
    uint8_t i;
    Xspi_IpGetDevSize(bus->host, busConfig->csNum);
    for (i = 0u; i < busConfig->csNum; i++) {
        bus->device[i].size = bus->host->devSize[i];
        bus->device[i].cs = i;
        bus->device[i].addrWidth = bus->host->memoryReadCmd.addrBytes;
        bus->device[i].offset = bus->totalSize;
        bus->device[i].regProto.sketch.proto = XSPI_BUS_REG_PROTO(
            bus->host->memoryReadCmd.proto.detail.comandLans,
            bus->host->memoryReadCmd.proto.detail.comamdRate,
            bus->host->memoryReadCmd.proto.detail.invertOpcodeEn);
        bus->device[i].sectorSize = 0x1000u;
        bus->device[i].info.readProto = bus->host->memoryReadCmd.proto;
        bus->device[i].info.writeProto = bus->host->memoryWriteCmd.proto;
        bus->device[i].info.eraseOpcode[0] =
            (bus->device[i].regProto.detail.invertOpcodeEn == 1u) ?
                XSPI_BUS_OP_BE_4K_4B : XSPI_BUS_OP_BE_4K;
        bus->device[i].info.readDummy = bus->host->memoryReadCmd.dummy;
        bus->device[i].info.writeDummy = bus->host->memoryWriteCmd.dummy;
        bus->device[i].info.statusDummy =
            (bus->device[i].regProto.detail.comandLans == XSPI_IP_OCTAL_LANS) ?
            ((bus->device[i].regProto.detail.invertOpcodeEn == 1u) ? 4u : 8u) : 0u;
        bus->device[i].info.pageSize = (bus->host->config.devType == XSPI_IP_HYPERRAM) ? 0u : 256u;
        bus->totalSize += bus->device[i].size;
    }
    bus->csNum = busConfig->csNum;
}

/***************************************************************************************************
 *                                  Global Function definitions                                   *
 **************************************************************************************************/

int32_t Xspi_BusInit(Xspi_BusHandleType *bus, Xspi_IpHostType *host, const Xspi_BusConfigType *busConfig)
{
    int32_t ret = XSPI_RET_OK;
    uint8_t i;

    /* param check */
    if ((NULL == bus) || (NULL == host) || (NULL == busConfig))
    {
        ret = XSPI_RET_PARAMETER_ERR;
    }
    else if (busConfig->csNum > XSPI_BUS_MAX_CS_NUM)
    {
        ret = XSPI_RET_UNSUPPORT;
    }
    else
    {
        /* Initial runtime params */
        /* PRQA S 0315 1 */
        (void) XSPI_MEMCLR(bus, sizeof(Xspi_BusHandleType));
        bus->host = host;
    }

    if (XSPI_RET_OK != ret)
    {
        /* Do nothing */
    }
    else if (bus->host->config.useRomConfig == true)
    {
        Xspi_BusRomConfigSetup(bus, busConfig);
    }
    else
    {
        /* Flash init flow */
        for (i = 0; (i < busConfig->csNum) && (ret == XSPI_RET_OK); i++)
        {
            if (XSPI_RET_OK != Xspi_IpRefclkSetup(host, XSPI_BUS_INIT_REFCLK_HZ))
            {
                ret = XSPI_RET_UNSUPPORT;
            }
            /* Step1. Detect devices by flash id */
            else if (XSPI_RET_OK != Xspi_BusFlashDetect(bus, i))
            {
                ret = XSPI_RET_UNSUPPORT;
            }
            /* Step2. Flash init based on parameters */
            else if (XSPI_RET_OK != Xspi_BusFlashInit(bus, i))
            {
                ret = XSPI_RET_FAILED;
            }
            /* Step3. Bus Training */
            else if (XSPI_RET_OK != Xspi_BusTraining(bus, i))
            {
                ret = XSPI_RET_FAILED;
            }
            else
            {
                /* Do nothing */
            }
        }

        if (XSPI_RET_OK != ret)
        {
            /* Do nothing */
        }
        else if (XSPI_RET_OK != Xspi_IpRfdSetup(bus->host))
        {
            ret = XSPI_RET_FAILED;
        }
        else
        {
            Xspi_IpPotoSetup(bus->host);
        }
    }

    return ret;
}

/* PRQA S 3206 1 */
int32_t Xspi_BusDeInit(Xspi_BusHandleType *bus)
{
    int32_t ret = XSPI_RET_OK;

    return ret;
}

int32_t Xspi_BusRead(Xspi_BusHandleType *bus, uint32_t address, uint8_t* buf, uint32_t size)
{
    int32_t ret = XSPI_RET_OK;
    uint32_t reaedDone = 0u;
    uint32_t overlapSize = 0u;
    uint8_t cs;

    while ((reaedDone < size) && (XSPI_RET_OK == ret))
    {
        cs = Xspi_BusMatchCsNum(bus, address + reaedDone, size - reaedDone, &overlapSize);
        if ((XSPI_BUS_MAX_CS_NUM == cs) || (overlapSize == 0u))
        {
            ret = XSPI_RET_PARAMETER_ERR;
        }
        else if (XSPI_RET_OK != Xspi_BusWaitIdle(bus, cs, XSPI_BUS_ERASE_TIMEOUT_US))
        {
            XSPI_LOG_ERROR("Wait idle failed\r\n");
            ret = XSPI_RET_BUSY;
        }
        else if (XSPI_RET_OK != Xspi_IpMemoryRead(bus->host, address + reaedDone,
                    &buf[reaedDone], overlapSize))
        {
            ret = XSPI_RET_FAILED;
        }
        else
        {
            reaedDone += overlapSize;
        }
    }

    return ret;
}

int32_t Xspi_BusWrite(Xspi_BusHandleType *bus, uint32_t address,
    const uint8_t* buf, uint32_t size)
{
    int32_t ret = XSPI_RET_OK;
    uint32_t writeDone = 0u;
    uint32_t overlapSize = 0u, writeSize;
    uint8_t cs = Xspi_BusMatchCsNum(bus, address, size, &overlapSize);
    uint32_t currentStartAddr = address;

    if ((XSPI_BUS_MAX_CS_NUM == cs) || (overlapSize == 0u))
    {
        ret = XSPI_RET_PARAMETER_ERR;
    }

    while ((writeDone < size) && (XSPI_RET_OK == ret))
    {
        writeSize = XSPI_MIN(size - writeDone, overlapSize);
        if (bus->device[cs].info.pageSize > 0u)
        {
            writeSize = XSPI_MIN(writeSize,
                bus->device[cs].info.pageSize - (currentStartAddr % bus->device[cs].info.pageSize));
        }
        if (bus->host->config.useRomConfig == true)
        {
            writeSize = XSPI_MIN(writeSize, bus->host->config.fifoSize);
        }

        if (XSPI_RET_OK != Xspi_BusWaitIdle(bus, cs, XSPI_BUS_WRITE_TIMEOUT_US))
        {
            XSPI_LOG_ERROR("Wait idle failed\r\n");
            ret = XSPI_RET_BUSY;
        }
        else if (XSPI_RET_OK != Xspi_BusWriteEnable(bus, cs, true))
        {
            XSPI_LOG_ERROR("Write enable failed\r\n");
            ret = XSPI_RET_FAILED;
        }
        else if (XSPI_RET_OK != Xspi_IpMemoryWrite(bus->host, currentStartAddr,
                    &buf[writeDone], writeSize))
        {
            XSPI_LOG_ERROR("Write failed\r\n");
            ret = XSPI_RET_FAILED;
        }
        else
        {
            writeDone += writeSize;
            currentStartAddr += writeSize;
            /*  Write to the tail address of the device*/
            if ((currentStartAddr == (bus->device[cs].offset + bus->device[cs].size))
                && (writeDone < size))
            {
                cs = Xspi_BusMatchCsNum(bus, currentStartAddr, size - writeDone, &overlapSize);
                if ((XSPI_BUS_MAX_CS_NUM == cs) || (overlapSize == 0u))
                {
                    ret = XSPI_RET_PARAMETER_ERR;
                }
            }
        }
    }
    return ret;
}

/* PRQA S 2755 1 */
int32_t Xspi_BusWriteSync(Xspi_BusHandleType *bus, uint32_t address,
    const uint8_t* buf, uint32_t size)
{
    int32_t ret = Xspi_BusWrite(bus, address, buf, size);
    if (XSPI_RET_OK == ret)
    {
        ret = Xspi_BusWaitBlockIdle(bus, address, size, XSPI_BUS_WRITE_TIMEOUT_US);
    }
    return ret;
}

int32_t Xspi_BusSectorErase(Xspi_BusHandleType *bus, uint32_t address, uint32_t size, uint32_t *eraseSize)
{
    int32_t ret = XSPI_RET_FAILED;
    uint32_t overlapSize = 0;
    uint8_t cs = Xspi_BusMatchCsNum(bus, address, size, &overlapSize);
    uint32_t eraseSizeRet = 0u;
    uint8_t opcode = 0u;
    Xspi_IpCommandType writeCmd;

    /* PRQA S 0315 1 */
    (void) XSPI_MEMCLR(&writeCmd, sizeof(Xspi_IpCommandType));

    if ((XSPI_BUS_MAX_CS_NUM == cs) || (overlapSize == 0u))
    {
        XSPI_LOG_ERROR("Match cs falied\r\n");
    }
    else if (XSPI_RET_OK != Xspi_BusMatchOptimumEraseCmd(bus, cs, address - bus->device[cs].offset,
                overlapSize, &opcode, &eraseSizeRet))
    {
        XSPI_LOG_ERROR("Match erase command failed\r\n");
    }
    else if (XSPI_RET_OK != Xspi_BusWaitIdle(bus, cs, XSPI_BUS_ERASE_TIMEOUT_US))
    {
        XSPI_LOG_ERROR("Wait idle failed\r\n");
    }
    else if (XSPI_RET_OK != Xspi_BusWriteEnable(bus, cs, true))
    {
        XSPI_LOG_ERROR("Write enable failed\r\n");
    }
    else
    {
        Xspi_IpCacheFlush(bus->host, address, eraseSizeRet);
        writeCmd.proto.sketch.proto = XSPI_IP_PROTO_DATA_DIS(bus->device[cs].regProto.sketch.proto);
        writeCmd.proto.sketch.opcode = opcode;
        writeCmd.dummy = 0u;
        writeCmd.addrBytes = bus->device[cs].addrWidth;

        if (XSPI_RET_OK != Xspi_IpCommandWrite(bus->host, &writeCmd, address, NULL_PTR, 0u))
        {
            XSPI_LOG_ERROR("Write erase command failed\r\n");
        }
        else
        {
            if (eraseSize != NULL)
            {
                *eraseSize = eraseSizeRet;
            }
            ret = XSPI_RET_OK;
        }
    }

    return ret;
}

int32_t Xspi_BusSectorEraseSync(Xspi_BusHandleType *bus, uint32_t address, uint32_t size, uint32_t *eraseSize)
{
    int32_t ret = Xspi_BusSectorErase(bus, address, size, eraseSize);
    if (XSPI_RET_OK == ret)
    {
        ret = Xspi_BusWaitBlockIdle(bus, address, *eraseSize, XSPI_BUS_ERASE_TIMEOUT_US);
    }
    return ret;
}

int32_t Xspi_BusErase(Xspi_BusHandleType *bus, uint32_t address, uint32_t size)
{
    uint32_t eraseDone = 0u;
    uint32_t eraseSize;

    while (eraseDone < size)
    {
        if (XSPI_RET_OK != Xspi_BusSectorErase(bus,
                address + eraseDone, size - eraseDone, &eraseSize))
        {
            break;
        }
        else
        {
            eraseDone += eraseSize;
        }
    }
    return (eraseDone < size) ? XSPI_RET_FAILED : XSPI_RET_OK;
}

int32_t Xspi_BusEraseSync(Xspi_BusHandleType *bus, uint32_t address, uint32_t size)
{
    int32_t ret = Xspi_BusErase(bus, address, size);
    if (XSPI_RET_OK == ret)
    {
        ret = Xspi_BusWaitBlockIdle(bus, address, size, XSPI_BUS_ERASE_TIMEOUT_US);
    }
    return ret;
}

int32_t Xspi_BusGetStatus(Xspi_BusHandleType *bus)
{
    int32_t ret = XSPI_RET_OK;
    uint8_t cs;
    for (cs = 0u; cs < bus->csNum; cs++)
    {
        ret = Xspi_BusGetBusyStatus(bus, cs);
        if (ret != XSPI_RET_OK) {
            break;
        }
    }

    return ret;
}

int32_t Xspi_BusGetBlockStatus(Xspi_BusHandleType *bus, uint32_t address, uint32_t size)
{
    int32_t ret = XSPI_RET_OK;
    uint32_t overlapSize = 0u;
    uint8_t cs;
    uint32_t currentStartAddr = address;
    uint32_t residualSize = size;

    while ((currentStartAddr < (address + size)) && (XSPI_RET_OK == ret))
    {
        cs = Xspi_BusMatchCsNum(bus, currentStartAddr, residualSize, &overlapSize);
        if (XSPI_BUS_MAX_CS_NUM == cs)
        {
            XSPI_LOG_ERROR("Match cs falied\r\n");
            ret = XSPI_RET_PARAMETER_ERR;
        }
        else if (NULL != bus->device[cs].info.getBankStatus)
        {
            ret = bus->device[cs].info.getBankStatus(bus, cs, currentStartAddr - bus->device[cs].offset, overlapSize);
        }
        else
        {
            ret = Xspi_BusGetBusyStatus(bus, cs);
        }
        currentStartAddr += overlapSize;
        residualSize -= overlapSize;
    }

    return ret;
}

/* PRQA S 3206 1 */
int32_t Xspi_BusSetProtectArea(Xspi_BusHandleType *bus, uint32_t addr, uint32_t size)
{
    int32_t ret = XSPI_RET_OK;

    return ret;
}

/* PRQA S 3206 1 */
int32_t Xspi_BusGetProtectStatus(Xspi_BusHandleType *bus, uint32_t addr, uint32_t size)
{
    int32_t ret = XSPI_RET_OK;

    return ret;
}

/* PRQA S 3206 1 */
int32_t Xspi_BusCancel(Xspi_BusHandleType *bus)
{
    return XSPI_RET_OK;
}

#ifdef CFG_PLATFORM_MCAL
#define FLS_STOP_SEC_CODE
#include "Fls_MemMap.h"
#endif /* #ifdef CFG_PLATFORM_MCAL */
#ifdef __cplusplus
}
#endif /* #ifdef __cplusplus */


/* End of file */
